import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
from plotly import tools
import plotly.express as px
import plotly.offline as py
import scipy.stats as stats
telcom = pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv', sep=',', encoding='latin1')
#Data Manipulation
#Replacing spaces with null values in total charges column
telcom['TotalCharges'] = telcom["TotalCharges"].replace(" ",np.nan)
#Dropping null values from total charges column which contain .15% missing data
telcom = telcom[telcom["TotalCharges"].notnull()]
telcom = telcom.reset_index()[telcom.columns]
#convert to float type
telcom["TotalCharges"] = telcom["TotalCharges"].astype(float)
#replace 'No internet service' to No for the following columns
replace_cols = [ 'OnlineSecurity', 'OnlineBackup', 'DeviceProtection',
'TechSupport','StreamingTV', 'StreamingMovies']
for i in replace_cols :
telcom[i] = telcom[i].replace({'No internet service' : 'No'})
#replace values
telcom["SeniorCitizen"] = telcom["SeniorCitizen"].replace({1:"Yes",0:"No"})
#Tenure to categorical column
def tenure_lab(telcom) :
if telcom["tenure"] <= 12 :
return "0-12"
elif (telcom["tenure"] > 12) & (telcom["tenure"] <= 24 ):
return "12-24"
elif (telcom["tenure"] > 24) & (telcom["tenure"] <= 48) :
return "24-48"
elif (telcom["tenure"] > 48) & (telcom["tenure"] <= 60) :
return "48-60"
elif telcom["tenure"] > 60 :
return "gt_60"
telcom["tenure_group"] = telcom.apply(lambda telcom:tenure_lab(telcom),
axis = 1)
#Separating churn and non churn customers
churn = telcom[telcom["Churn"] == "Yes"]
not_churn = telcom[telcom["Churn"] == "No"]
#Separating catagorical and numerical columns
Id_col = ['customerID']
target_col = ["Churn"]
cat_cols = telcom.nunique()[telcom.nunique() < 6].keys().tolist()
cat_cols = [x for x in cat_cols if x not in target_col]
num_cols = [x for x in telcom.columns if x not in cat_cols + target_col + Id_col]
telcom.head()
| customerID | gender | SeniorCitizen | Partner | Dependents | tenure | PhoneService | MultipleLines | InternetService | OnlineSecurity | ... | TechSupport | StreamingTV | StreamingMovies | Contract | PaperlessBilling | PaymentMethod | MonthlyCharges | TotalCharges | Churn | tenure_group | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 7590-VHVEG | Female | No | Yes | No | 1 | No | No phone service | DSL | No | ... | No | No | No | Month-to-month | Yes | Electronic check | 29.85 | 29.85 | No | 0-12 |
| 1 | 5575-GNVDE | Male | No | No | No | 34 | Yes | No | DSL | Yes | ... | No | No | No | One year | No | Mailed check | 56.95 | 1889.50 | No | 24-48 |
| 2 | 3668-QPYBK | Male | No | No | No | 2 | Yes | No | DSL | Yes | ... | No | No | No | Month-to-month | Yes | Mailed check | 53.85 | 108.15 | Yes | 0-12 |
| 3 | 7795-CFOCW | Male | No | No | No | 45 | No | No phone service | DSL | Yes | ... | Yes | No | No | One year | No | Bank transfer (automatic) | 42.30 | 1840.75 | No | 24-48 |
| 4 | 9237-HQITU | Female | No | No | No | 2 | Yes | No | Fiber optic | No | ... | No | No | No | Month-to-month | Yes | Electronic check | 70.70 | 151.65 | Yes | 0-12 |
5 rows × 22 columns
Я выбрала базу данных клиентов Оператора универсальных услуг связи. В этой базе так же отмечено, ушел ли клиент от этого оператора или остался.
Идеей моего проекта было понять, почему клиенты уходят и как это предотвратить, как исправить ситуацию.
Сначала я решила посмотреть, с какими данными я работаю и как соотносятся разные величины для оставшихся и ушедших клиентов.
Первым параметром было Количество ушедших и оставшихся клиентов.
#labels
lab = telcom["Churn"].value_counts().keys().tolist()
lab = ["Остались","Ушло" ]
#values
val = telcom["Churn"].value_counts().values.tolist()
trace = go.Pie(labels = lab ,
values = val ,
marker = dict(colors = [px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
line = dict(color = "white",
width = 1.3)
),
rotation = 94,
hoverinfo = "label+value+text"
)
layout = go.Layout(dict(title = "Количество ушедших и оставшихся клиентов",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
)
)
data = [trace]
fig = go.Figure(data = data,layout = layout)
py.iplot(fig)
Большее количество клиентов остаются в кампании, но 26.6 процентов все-таки уходят
Далее я разделила всех клиентов на оставшихся и ушедших и смотрела по разным категориям.
#function for pie plot for customer attrition types
def plot_pie(column) :
trace1 = go.Pie(values = churn[column].value_counts().values.tolist(),
labels = churn[column].value_counts().keys().tolist(),
hoverinfo = "label+percent+name",
domain = dict(x = [0,.48]),
name = "Churn Customers",
marker = dict(colors = [px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]
,px.colors.sequential.Sunsetdark[2]
,px.colors.sequential.Sunsetdark[1]],
line = dict(width = 2,
color = "rgb(243,243,243)")
),
hole = .6
)
trace2 = go.Pie(values = not_churn[column].value_counts().values.tolist(),
labels = not_churn[column].value_counts().keys().tolist(),
hoverinfo = "label+percent+name",
marker = dict(
line = dict(width = 2,
color = "rgb(243,243,243)")
),
domain = dict(x = [.52,1]),
hole = .6,
name = "Non churn customers"
)
if column == "gender":
col_name = "Гендерное распределение клиентов "
elif column == "SeniorCitizen":
col_name = " Является ли клиент пожилым или нет"
elif column == "Partner":
col_name = " Есть ли у клиента партнер или нет"
elif column == "Dependents":
col_name = " Есть ли у клиента иждевенцы или нет"
elif column == "PhoneService":
col_name = " Есть ли у клиента телефон или нет"
elif column == "MultipleLines":
col_name = " Есть ли у клиента несколько линий или нет"
elif column == "InternetService":
col_name = "Интернет-провайдер клиента"
elif column == "OnlineSecurity":
col_name = " Есть ли у клиента онлайн-безопасность или нет"
elif column == "OnlineBackup":
col_name = "Есть ли у клиента онлайн-резервное копирование или нет"
elif column == "DeviceProtection":
col_name = " Есть ли у клиента защита устройства или нет"
elif column == "TechSupport":
col_name = " Есть ли у клиента техническая поддержка или нет"
elif column == "StreamingTV":
col_name = "Есть ли у клиента потоковое телевидение или нет"
elif column == "StreamingMovies":
col_name = " Есть ли у клиента потоковое кино или нет"
elif column == "Contract":
col_name = "Срок договора заказчика"
elif column == "PaperlessBilling":
col_name = "Есть ли у клиента безбумажный счет или нет"
elif column == "PaymentMethod":
col_name = " Способ оплаты клиента"
elif column == "tenure_group":
col_name = " Количество месяцев, в течение которых клиент находился в компании"
#elif column == "churn_rate":
# col_name = "churn_rate"
layout = go.Layout(dict(title = col_name,
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
annotations = [dict(text = "Ушедшие клиенты",
font = dict(size = 17),
showarrow = False,
x = .13, y = .5),
dict(text = "Оставшиеся клиенты",
font = dict(size = 17),
showarrow = False,
x = .88,y = .5
)
]
)
)
data = [trace1,trace2]
fig = go.Figure(data = data,layout = layout)
py.iplot(fig)
#for all categorical columns plot pie
for i in cat_cols[0:-1] :
plot_pie(i)
Гендерное распределение клиентов:
Является ли клиент пожилым или нет
Есть ли у клиента партнер или нет
Есть ли у клиента иждевенцы или нет
Есть ли у клиента телефонный сервис или нет
Есть ли у клиента несколько линий или нет
Интернет-провайдер клиента
Есть ли у клиента онлайн-безопасность или нет
Есть ли у клиента онлайн-резервное копирование или нет
Есть ли у клиента защита устройства или нет
Есть ли у клиента техническая поддержка или нет
Есть ли у клиента потоковое телевидение и фильмы или нет
Срок договора заказчика
Есть ли у клиента безбумажный счет или нет
Способ оплаты клиента
cols = ["OnlineSecurity", "OnlineBackup", "DeviceProtection", "TechSupport", "StreamingTV", "StreamingMovies","PhoneService" ]
telcom1 = pd.melt(telcom[telcom["InternetService"] != "No"][cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(telcom1, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество людей, которые пользуются каждой из услуг",
labels = ["Количество", "Услуга"])
fig.show()
Наиболее популярными услугами оказались телефон, просмотр телевизора и фильмов
cols = ["OnlineSecurity", "OnlineBackup", "DeviceProtection", "TechSupport", "StreamingTV", "StreamingMovies","PhoneService" ]
telcom1 = pd.melt(telcom[telcom["InternetService"] == "No"][cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(telcom1, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество людей, которые пользуются каждой из услуг, если нет интернета",
labels = ["Количество", "Услуга"])
fig.show()
Надо обратить внимание на то, что если у человека не подключен интернет, то он не пользуется остальными услугами либо без интернета он не может ими пользоваться.
lab =telcom.groupby('SeniorCitizen')["SeniorCitizen"].value_counts().keys().tolist()
lab = ["Молодые","Пожилые" ]
#values
val = telcom.groupby('SeniorCitizen')["SeniorCitizen"].value_counts().values.tolist()
trace = go.Pie(labels = lab ,
values = val ,
marker = dict(colors = [px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
line = dict(color = "white",
width = 1.3)
),
#rotation = 90,
hoverinfo = "label+value+text",
)
layout = go.Layout(dict(title = "Количество клиентов пожилого возраста",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
)
)
data = [trace]
fig = go.Figure(data = data,layout = layout)
py.iplot(fig)
lab =telcom.groupby('SeniorCitizen')["SeniorCitizen"].value_counts().keys().tolist()
lab
[('No', 'No'), ('Yes', 'Yes')]
Пожилых людей не так много, но
lab = telcom[ (telcom['SeniorCitizen']=='Yes')].groupby('Churn')["Churn"].value_counts().keys().tolist()
lab = ["Остались","Ушло" ]
#values
val = telcom[ (telcom['SeniorCitizen']=='Yes')].groupby('Churn')["Churn"].value_counts().values.tolist()
trace = go.Pie(labels = lab ,
values = val ,
marker = dict(colors = [px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
line = dict(color = "white",
width = 1.3)
),
#rotation = 90,
hoverinfo = "label+value+text"
)
layout = go.Layout(dict(title = "Количество ушедших и оставшихся клиентов пожилого возраста",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
)
)
data = [trace]
fig = go.Figure(data = data,layout = layout)
py.iplot(fig)
Большая доля пенсионеров уходит
Посмотрим, почему они могут уходить.
Во-первых, посмотрим, какими услугами они пользуются:
df1 = telcom[(telcom.InternetService != "No") &(telcom['SeniorCitizen']=='Yes')]
df2 = pd.melt(df1[cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(df2, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество пожилых людей в зависимости от услуг",
labels = ["Количество", "Услуга"])
fig.show()
df1 = telcom[(telcom.InternetService != "No") &(telcom['SeniorCitizen']=='Yes')&(telcom['Churn']=='Yes')]
df2 = pd.melt(df1[cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(df2, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество пожилых людей, которые ушли от оператора в зависимости от услуг",
labels = ["Количество", "Услуга"])
fig.show()
Оказалось, что большинство пожилых людей, которые ушли из кампании, использовали телевидение и смотрели фильмы.
Вывод
Можно сделать вывод о том, что люди пожилого возраста уходят из кампании, потому что они в основном смотрят телевизор и фильм и интернет им не нужен и они не хотят тратить деньги на лишние услуги.
Для решения проблемы можно:
telcom.groupby(['PaymentMethod','Churn'])['PaymentMethod'].count()
PaymentMethod Churn
Bank transfer (automatic) No 1284
Yes 258
Credit card (automatic) No 1289
Yes 232
Electronic check No 1294
Yes 1071
Mailed check No 1296
Yes 308
Name: PaymentMethod, dtype: int64
#cusomer attrition in tenure groups
tg_ch2 = churn['PaymentMethod'].value_counts().reset_index()
tg_ch2.columns = ["PaymentMethod","count"]
tg_nch2 = not_churn['PaymentMethod'].value_counts().reset_index()
tg_nch2.columns = ["PaymentMethod","count"]
#bar - churn
trace1 = go.Bar(x = tg_ch2["PaymentMethod"] , y = tg_ch2["count"],
name = "Ушедшие клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[3],line = dict(width = .5)),
opacity = .9)
#bar - not churn
trace2 = go.Bar(x = tg_nch2["PaymentMethod"] , y = tg_nch2["count"],
name = "Оставшиеся клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[0],line = dict(width = .5)),
opacity = .9)
layout = go.Layout(dict(title = "Отток пользователей в зависимости от способа оплаты",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Вид оплаты",
zerolinewidth=1,ticklen=5,gridwidth=2),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Количество",
zerolinewidth=1,ticklen=5,gridwidth=2),
)
)
data = [trace1,trace2]
fig = go.Figure(data=data,layout=layout)
py.iplot(fig)
У оставшихся клиентов все виды оплат распределились одинаковым образом (примерно 25%)
Сервис с оплатой по электронному чеку имеет наибольший отток. Клиетам может не нравится сервис с опатой по электоронному чеку, хначит они уходят из кампании
Вывод
Чтобы избавиться от проблемы необходимо улучшить сервис с оплатой по электронному чеку.
correlation = telcom.corr()
#tick labels
matrix_cols = ["Всего платежей","Ежемесячные платежи","Cрок пребывания"]#correlation.columns.tolist()
#convert to array
corr_array = np.array(correlation)
#Plotting
trace = go.Heatmap(z = corr_array,
x = matrix_cols,
y = matrix_cols,
colorscale = "sunsetdark",
colorbar = dict(title = "Коэффициент корреляции Пирсона",
titleside = "right"
) ,
)
layout = go.Layout(dict(title = "Матрица корреляции для переменных",
autosize = False,
height = 720,
width = 800,
margin = dict(r = 0 ,l = 210,
t = 25,b = 210,
),
yaxis = dict(tickfont = dict(size = 15)),
xaxis = dict(tickfont = dict(size = 15))
)
)
data = [trace]
fig = go.Figure(data=data,layout=layout)
py.iplot(fig)
Зависят друг от друга: Срок пребывания и Общая сумма платежей
Не зависят: ежемесячный платеж и общая сумма платежа
Чтобы лучше понять, как соотносятся величины, потроим точечную диаграмму.
#функция матрицы рассеяния для числовых столбцов в данных
def scatter_matrix(df) :
df = df.sort_values(by = "Churn" ,ascending = True)
classes = df["Churn"].unique().tolist() # разделили на 2 группы ['No', 'Yes']
classes
class_code = {classes[k] : k for k in range(2)} #присвоили группе код {'No': 0, 'Yes': 1}
class_code
color_vals = [class_code[cl] for cl in df["Churn"]] # переделали столбец на цифры
color_vals
pl_colorscale = [px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]]
pl_colorscale
text = [df.loc[k,"Churn"] for k in range(len(df))]
text
trace = go.Splom(dimensions = [dict(label = "Срок пребывания",
values = df["tenure"]),
dict(label = 'Ежемесячные платежи',
values = df['MonthlyCharges']),
dict(label = 'Всего платежей',
values = df['TotalCharges'])],
text = text,
marker = dict(color = color_vals,
colorscale = pl_colorscale,
size = 3,
showscale = False,
line = dict(width = .1,
color='rgb(230,230,230)'
)
)
)
axis = dict(showline = True,
zeroline = False,
gridcolor = "#fff",
ticklen = 4
)
layout = go.Layout(dict(title =
"Матрица точечной диаграммы для числовых столбцов для оттока клиентов",
autosize = False,
height = 800,
width = 800,
dragmode = "select",
hovermode = "closest",
plot_bgcolor = 'rgba(240,240,240, 0.95)',
xaxis1 = dict(axis),
yaxis1 = dict(axis),
xaxis2 = dict(axis),
yaxis2 = dict(axis),
xaxis3 = dict(axis),
yaxis3 = dict(axis),
)
)
data = [trace]
fig = go.Figure(data = data,layout = layout )
py.iplot(fig)
#scatter plot matrix
scatter_matrix(telcom)
Зависимость всей суммы платежей от срока пребывания
Зависимость всей суммы платежей от ежемесечного платежа
Зависимость ежемесячного платежа от срока пребывания
#cusomer attrition in tenure groups
tg_ch = churn["tenure_group"].value_counts().reset_index()
tg_ch.columns = ["tenure_group","count"]
tg_nch = not_churn["tenure_group"].value_counts().reset_index()
tg_nch.columns = ["tenure_group","count"]
#bar - churn
trace1 = go.Bar(x = tg_ch["tenure_group"] , y = tg_ch["count"],
name = "Ушедшие клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[3],line = dict(width = .5)),
opacity = .9)
#bar - not churn
trace2 = go.Bar(x = tg_nch["tenure_group"] , y = tg_nch["count"],
name = "Оставшиеся клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[0],line = dict(width = .5)),
opacity = .9)
layout = go.Layout(dict(title = "Отток пользователей в разрезе количества месяцев",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Срок пребывания, месяцы",
zerolinewidth=1,ticklen=5,gridwidth=2),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Количество",
zerolinewidth=1,ticklen=5,gridwidth=2),
)
)
data = [trace1,trace2]
fig = go.Figure(data=data,layout=layout)
py.iplot(fig)
Отток пользователей в разрезе количества месяцев
avg_tgc = telcom.groupby(["tenure_group","Churn"])[["MonthlyCharges",
"TotalCharges"]].mean().reset_index()
#function for tracing
def mean_charges(column,aggregate) :
if aggregate == "Yes":
color= px.colors.sequential.Sunsetdark[3]
name1="Ушли"
else:
color= px.colors.sequential.Sunsetdark[0]
name1="Остались"
tracer = go.Bar(x = avg_tgc[avg_tgc["Churn"] == aggregate]["tenure_group"],
y = avg_tgc[avg_tgc["Churn"] == aggregate][column],
name = name1,marker = dict(color = color,line = dict(width = 1)),
text = "Churn"
)
return tracer
#function for layout
def layout_plot(title,xaxis_lab,yaxis_lab) :
layout = go.Layout(dict(title = title,
plot_bgcolor = "rgb(243,243,243)",
#colorway = "sunsetdark",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',title = xaxis_lab,
zerolinewidth=1,ticklen=5,gridwidth=2),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',title = yaxis_lab,
zerolinewidth=1,ticklen=5,gridwidth=2),
)
)
return layout
#plot1 - mean monthly charges by tenure groups
trace1 = mean_charges("MonthlyCharges","Yes")
trace2 = mean_charges("MonthlyCharges","No")
layout1 = layout_plot("Среднемесячные расходы по группам срока пребывания",
"Срок пребывания в компании, месяцы","Среднемесячные расходы, доллары")
data1 = [trace1,trace2]
fig1 = go.Figure(data=data1,layout=layout1)
#plot2 - mean total charges by tenure groups
trace3 = mean_charges("TotalCharges","Yes")
trace4 = mean_charges("TotalCharges","No")
layout2 = layout_plot("Средние общие расходы по группам срока пребывания",
"Срок пребывания в компании, месяцы","Всего расходов, доллары")
data2 = [trace3,trace4]
fig2 = go.Figure(data=data2,layout=layout2)
py.iplot(fig1)
py.iplot(fig2)
Среднемесячные расходы по группам срока пребывания
Средние общие расходы по группам срока пребывания
import plotly.figure_factory as ff
#function for histogram for customer attrition types
def histogram(column) :
trace1 = go.Histogram(x = churn[column],
histnorm= "percent",
name = "Ушедшие клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[3],
line = dict(width = .5
)
),
opacity = .9
)
trace2 = go.Histogram(x = not_churn[column],
histnorm = "percent",name = "Оставшиеся клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[0],
line = dict(width = .5)
),
opacity = .9
)
data = [trace1,trace2]
if column == "tenure":
col_name = "Распределение по сроку пребывания"
name = "Cрок пребывания"
elif column == "MonthlyCharges":
col_name = "Распределение по ежемесячным платежам"
name = "Ежемесячным платеж"
elif column == "TotalCharges":
col_name = "Распределение по общему колличесву платежей"
name = "Общее колличесво платежей"
layout = go.Layout(dict(title =col_name ,
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = name,
zerolinewidth=1,
ticklen=5,
gridwidth=2
),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Процент",
zerolinewidth=1,
ticklen=5,
gridwidth=2
),
)
)
fig = go.Figure(data=data,layout=layout)
#fig2 = ff.create_distplot([churn[column],not_churn[column]],["Churn Customers","Non churn customers"],
#colors = px.colors.sequential.Sunsetdark, bin_size=[.1, .25, .5, 1], show_rug=False)
py.iplot(fig)
#for all categorical columns plot histogram
for i in num_cols :
histogram(i)
Распределение по сроку пребывания
Распределение по ежемесячным платежам
Распределение по общему колличесву платежей
Посмотрим, какими услугами польщовались клиенты с высокой оплатой:
df1 = telcom[(telcom.InternetService != "No") &(telcom['MonthlyCharges']>=68)]
df2 = pd.melt(df1[cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(df2, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество людей, у которых ежемесячная оплата > 68 долларов",
labels = ["Количество", "Услуга"])
fig.show()
Посмотрим, какими услугами пользовались клиенты с высокой оплатой, котопые все-таки ушли из кампании:
df1 = telcom[(telcom.InternetService != "No") &(telcom['MonthlyCharges']>=68)&(telcom['Churn']=='Yes')]
df2 = pd.melt(df1[cols]).rename({'value': 'Пользуются услугой'}, axis=1).rename({'variable': 'Услуга'}, axis=1)
fig = px.histogram(df2, x="Услуга", color="Пользуются услугой",
color_discrete_sequence=[px.colors.sequential.Sunsetdark[0],px.colors.sequential.Sunsetdark[3]],
title = "Колличество людей, у которых ежемесячная оплата > 68 долларов, которые ушли от оператора",
labels = ["Количество", "Услуга"])
fig.show()
Как и для пожилых людей получилось, что самыми популярными услунами оказались телевидение и фильмы и телефон.
Вывод
Надо улучшать сервис с телевидением и фильмами.
#cusomer attrition in tenure groups
tg_ch2 = churn['Contract'].value_counts().reset_index()
tg_ch2.columns = ["Contract","count"]
tg_nch2 = not_churn['Contract'].value_counts().reset_index()
tg_nch2.columns = ["Contract","count"]
#bar - churn
trace1 = go.Bar(x = tg_ch2["Contract"] , y = tg_ch2["count"],
name = "Ушедшие клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[3],line = dict(width = .5)),
opacity = .9)
#bar - not churn
trace2 = go.Bar(x = tg_nch2["Contract"] , y = tg_nch2["count"],
name = "Оставшиеся клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[0],line = dict(width = .5)),
opacity = .9)
layout = go.Layout(dict(title = "Отток пользователей в зависимости от длительности контракта",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Длительность контракта",
zerolinewidth=1,ticklen=5,gridwidth=2),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Количество",
zerolinewidth=1,ticklen=5,gridwidth=2),
)
)
data = [trace1,trace2]
fig = go.Figure(data=data,layout=layout)
py.iplot(fig)
y0 = churn["tenure"]
y1 = not_churn["tenure"]
fig = go.Figure()
fig.add_trace(go.Box(y=y0, x=churn["Contract"], name='Ушли',
marker_color = px.colors.sequential.Sunsetdark[3]))
fig.add_trace(go.Box(y=y1, x=not_churn["Contract"], name = 'Остались',
marker_color = px.colors.sequential.Sunsetdark[0]))
fig.show()
Наибольший отток клиентов происходит при заключении месячного договора. Вероятно, что это связано с тем, что люди сразу не рассчитавают на долгосрочный период пребывания в кампании.
Контракты сроком на один и два года, вероятно, имеют договорные штрафы, и, следовательно, клиенты должны ждать до конца срока действия контракта.
Вывод
Надо лучше продвигать годовые и особенно 2-х летние контракты, чтобы больше людей преобретали их и дольше оставались в кампании.
На сколько люди оставались после заключения каждого вида договора:
f= plt.figure(figsize=(12,5))
ax=f.add_subplot(121)
sns.distplot(churn[churn['Contract']== "Month-to-month"]["tenure"],color = '#fcde9c',hist = True,ax = ax)
ax.set_title(' test')
ax=f.add_subplot(122)
sns.distplot(not_churn[not_churn['Contract']== "Month-to-month"]["tenure"],color = '#e34f6f',hist = True,ax=ax)
ax.set_title('control')
C:\Users\marus\anaconda3\lib\site-packages\seaborn\distributions.py:2551: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). C:\Users\marus\anaconda3\lib\site-packages\seaborn\distributions.py:2551: FutureWarning: `distplot` is a deprecated function and will be removed in a future version. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms).
Text(0.5, 1.0, 'control')
Проверим с помощью ку-ку плота, насколько распределения нормальные
stats.probplot(churn[churn['Contract']== "Month-to-month"]["tenure"], dist="norm", plot=plt)
plt.show()
stats.probplot(not_churn[not_churn['Contract']== "Month-to-month"]["tenure"], dist="norm", plot=plt)
plt.show()
Проверим с помощью критерия Шапиро-Уилка
#проверка с помощью критерия Шапиро-Уилка
groups = [churn[churn['Contract']== "Month-to-month"]["tenure"],not_churn[not_churn['Contract']== "Month-to-month"]["tenure"]]
for group in groups:
W_value,p_value = stats.shapiro(group)
if p_value > 0.01:
print('Normal','W=',round(W_value,4),'p-value',round(p_value,4))
else:
print('Not normal','W=',round(W_value,4),'p-value',round(p_value,4))
Not normal W= 0.8016 p-value 0.0 Not normal W= 0.895 p-value 0.0
Распределение не нормальное. Используем критерий сравнения Манна-Уитни.
def U2 (a,b,rank_a,rank_b):
return min((rank_a - (a.count() * (a.count() + 1))/2),(rank_b - (b.count() * (b.count() + 1))/2))
def MannWhitney(a,b,rank_a,rank_b):
U = min((rank_a - (a.count() * (a.count() + 1))/2),(rank_b - (b.count() * (b.count() + 1))/2))
m_u = (a.count()*b.count())/2
sigma_u = np.sqrt((a.count()*b.count())*(a.count()+ b.count()+1)/12)
z_score = (U - m_u)/sigma_u
p_val = stats.norm.cdf(z_score) * 2
if p_val > 0.05:
print('Нет статистически значимой разницы и оснований отвергнуть гипотезу H0','\nU-критерий:',U,
'\nz_score',z_score,'\np-value',round(p_val,4))
else:
print('Есть статистически значимая разница, гипотеза H0 отвергается','\nU-критерий:',U,
'\nz_score',z_score,'\np-value',round(p_val,4))
return
Из двух выборок получим одну,отсортируем и затем расставим ранги. Максимальное значение в группе имеет первый ранг и так далее.
telcomC = telcom[(telcom['Churn']== "Yes") & (telcom['Contract']== "Month-to-month")]
#telcomCt["a"]= telcomC[['tenure']].dropna()
telcomN = telcom[(telcom['Churn']== "No") & (telcom['Contract']== "Month-to-month")]
#telcomNt["b"]= telcomN[['tenure']].dropna(axis=0 )
data = pd.concat([telcomC[['tenure']].dropna(),telcomN[['tenure']].dropna(axis=0 )],axis=1).drop(['tenure'], axis=1)
df1 = data.stack().reset_index(-1).iloc[:, ::-1]
df1.columns = ['value', 'group']
df1['rank'] = df1['value'].rank(ascending = False, numeric_only = True)
Дальше необходимо вычислить сумму рангов
df1.groupby('group')['rank'].sum()
a = df1[df1['group'] == 'a']['value']
b = df1[df1['group'] == 'b']['value']
rank_a = df1[df1['group'] == 'a']['rank'].sum()
rank_b = df1[df1['group'] == 'b']['rank'].sum()
print('U-критерий =',U2(a,b,rank_a,rank_b))
MannWhitney(a,b,rank_a,rank_b)
stat, p_val = stats.mannwhitneyu(a,b,alternative = 'two-sided')
if p_val > 0.05:
print('Статистически значимой разницы нет','\np-value',round(p_val,4),'\nU-критерий',stat)
else:
print('Разница статистически значима','\np-value',round(p_val,4),'\nU-критерий',stat)
Вывод
Получаем, что есть статистически значимая разница между
#cusomer attrition in tenure groups
tg_ch2 = churn['InternetService'].value_counts().reset_index()
tg_ch2.columns = ["InternetService","count"]
tg_nch2 = not_churn['InternetService'].value_counts().reset_index()
tg_nch2.columns = ["InternetService","count"]
#bar - churn
trace1 = go.Bar(x = tg_ch2["InternetService"] , y = tg_ch2["count"],
name = "Ушедшие клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[3],line = dict(width = .5)),
opacity = .9)
#bar - not churn
trace2 = go.Bar(x = tg_nch2["InternetService"] , y = tg_nch2["count"],
name = "Оставшиеся клиенты",
marker = dict(color = px.colors.sequential.Sunsetdark[0],line = dict(width = .5)),
opacity = .9)
layout = go.Layout(dict(title = "Отток пользователей в зависимости от вида интернет-соединения",
plot_bgcolor = "rgb(243,243,243)",
paper_bgcolor = "rgb(243,243,243)",
xaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Вид интернет-соединения",
zerolinewidth=1,ticklen=5,gridwidth=2),
yaxis = dict(gridcolor = 'rgb(255, 255, 255)',
title = "Количество",
zerolinewidth=1,ticklen=5,gridwidth=2),
)
)
data = [trace1,trace2]
fig = go.Figure(data=data,layout=layout)
py.iplot(fig)
Видно, что большинство людей, которые ушли из кампании пользовались оптоволоконным соединением.
Проверим гипотезу о том, что доли для разных линий связи различаются.
Н0: Доли никак не различаются, тип соединения не влияет на отток.
Н1: Доли различаются.
Критерий Хи-квадарт позволяет нам сравнить отношения количества успеха к неуспехам в двух таблицах.
internet = telcom[(telcom["InternetService"]!="No")][["InternetService", "Churn"]]
internet
| InternetService | Churn | |
|---|---|---|
| 0 | DSL | No |
| 1 | DSL | No |
| 2 | DSL | Yes |
| 3 | DSL | No |
| 4 | Fiber optic | Yes |
| ... | ... | ... |
| 7027 | DSL | No |
| 7028 | Fiber optic | No |
| 7029 | DSL | No |
| 7030 | Fiber optic | Yes |
| 7031 | Fiber optic | No |
5512 rows × 2 columns
table = pd.crosstab(
internet['InternetService'],
internet['Churn'],
margins = True
)
table
table1 = pd.crosstab(
internet['InternetService'],
internet['Churn']
)
table
| Churn | No | Yes | All |
|---|---|---|---|
| InternetService | |||
| DSL | 1957 | 459 | 2416 |
| Fiber optic | 1799 | 1297 | 3096 |
| All | 3756 | 1756 | 5512 |
def Chisq(table):
expected = []
obs1 = np.append(table.iloc[0][0:2].values, table.iloc[1][0:2].values)
rows = table.iloc[0:2,2].values
cols = table.iloc[2,0:2].values
total = table.loc['All','All']
for count in range(2):
for column in cols:
expected.append((column*rows[count])/total)
obs = obs1 + 0.5 * np.sign(expected - obs1)
return sum(((obs - expected)**2)/expected)
Chisq(table)
326.6008511107608
p_value = 1 - stats.chi2.cdf(Chisq(table),1)
if p_value > 0.05:
print('Не можем отвергнуть нулевую гипотезу о том, что доли не имеют значимого различия', '\nХи-квадрат критерий=',Chisq(table),'\np-value = ',p_value)
else:
print('Не можем принять нулевую гипотезу о том, что доли не имеют значимого различия','\nХи-квадрат критерий=',Chisq(table),'\np-value = ',p_value)
Не можем принять нулевую гипотезу о том, что доли не имеют значимого различия Хи-квадрат критерий= 326.6008511107608 p-value = 0.0
Вывод
Не можем принять нулевую гипотезу о том, что доли не имеют значимого различия.
Тип соединения влияет на отток.